package com.mamingchao.concurrent.thread_communication2;

import java.util.Stack;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 这里用 ReentrantLock 的condition。相比与synchronized 的就一个 线程等待队列，
 * ReentrantLock 一个conditon 就是一个线程的等待队列，两个就是两个等待队列。
 * 在put 方法了，执行了producer.await(),那么生产者（因为执行put方法的，肯定是生产者线程）的 线程
 * 就会到producer这个 condition 对应的线程等待队列里等待
 *
 * 在get 方法了，执行了consumer.await(),那么消费者（因为执行get方法的，肯定是消费者线程）的 线程
 * 就会到consumer这个 condition 对应的线程等待队列里等待
 *
 * 相当于把 线程分组了；这样叫醒线程的时候，就可以指定线程等待队列去叫醒了
 *
 * Created by mamingchao on 2020/9/20.
 */
public class SyncContainerWithReentrantLock {

    ReentrantLock lock = new ReentrantLock();
    Condition consumer = lock.newCondition();
    Condition producer = lock.newCondition();

    /*
        容器固定容量 为10
     */
    final int capacity = 8;

    Stack stackContainer = new Stack();

    /**
     * 如果容器已满，阻塞等待
     * 容器的写方法
     * @param  o
     * @return true false
     */
    public void put(Object o) {
        /**
         * 想想为什么用while ，不用if？
         *   当 容器满了的时候，线程wait 就停住了；然后被生产者 notify后，往下走，就会再走到while check一下，因为怕这个时候别的线程 往容器里放了，
         *   所以还需要check一下
         *   这里 比如容器满了，消费者取了一个，notifyAll了，然后多个生产者线程同时往下走，我们知道 应该只能有一个生产者 能真正的 走下去
         *
         *   这个地方 用while 还是用if，还需要再想想
         */
        lock.lock();
        try {
            while (stackContainer.size() == capacity) {
                producer.await();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        stackContainer.push(o);
        consumer.signalAll();

        lock.unlock();

    }


    /**
     * 如果容器已空，消费者阻塞
     * 容器的读方法
     * @return true false
     */
    public synchronized Object get() {

        lock.lock();

        while (stackContainer.empty()) {
            try {
                consumer.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        Object result = stackContainer.pop();
        producer.signalAll();
        lock.unlock();
        return result;
    }


    public static void main(String[] args) {
        SyncContainerWithReentrantLock sc = new SyncContainerWithReentrantLock();

        for (int i = 0; i < 20; i++) {
            String threadName = "put thread--" + i;
            String value = i + "";
            new Thread(()->{
                sc.put(value);
                System.out.println(threadName + "  has put in " + value);
            }).start();
        }

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (int i = 0; i < 20; i++) {
            String threadName = "get thread--" + i;
            new Thread(()->{
                Object v = sc.get();
                System.out.println(threadName + " has get " + v);
            }).start();
        }
    }
}
